const PENDING = "pending"
const FULFILLED = "fulfilled"
const REJECTED = "rejected"

class MyPromise {
  constructor(executor) {
    // 捕获执行器错误
    try {
      executor(this.resolve, this.reject)
    } catch(e) {
      this.reject(e)
    }
  }

  status = PENDING
  value = undefined
  reason = undefined
  sucessCallbackList = [] // 成功回调数组
  failCallbackList = [] // 失败回调数组

  resolve = (value) => {
    if (this.status !== PENDING) return
    this.status = FULFILLED
    this.value = value
    while (this.sucessCallbackList.length) this.sucessCallbackList.shift()()
  }

  reject = (reason) => {
    if (this.status !== PENDING) return
    this.status = REJECTED
    this.reason = reason
    while (this.failCallbackList.length) this.failCallbackList.shift()()
  }
  
  then(sucessCallback, failCallback) {
    sucessCallback = sucessCallback ? sucessCallback : value => value
    failCallback = failCallback ? failCallback : reason => { throw reason }
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            let x = sucessCallback(this.value)
            // 判断 x 是promise对象还是普通值
            resolvePromsie(promise2, x, resolve, reject)
          } catch(e) {
            reject(e)
          }
        }, 0);
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = failCallback(this.reason)
            resolvePromsie(promise2, x, resolve, reject)
          } catch(e) {
            reject(e)
          }
        }, 0);
      } else {
        // 等待状态 异步
        this.sucessCallbackList.push(() => {
          setTimeout(() => {
            try {
              let x = sucessCallback(this.value)
              resolvePromsie(promise2, x, resolve, reject)
            } catch(e) {
              reject(e)
            }
          }, 0);
        })
        this.failCallbackList.push(() => {
          setTimeout(() => {
            try {
              let x = failCallback(this.reason)
              resolvePromsie(promise2, x, resolve, reject)
            } catch(e) {
              reject(e)
            }
          }, 0);
        })
      }
    })
    return promise2
  }

  finally(callback) {
    // 返回 promise 对象
    return this.then(value => { 
      return MyPromise.resolve(callback()).then(() => value)
    }, reason => { 
      return MyPromise.resolve(callback()).then(() => { throw reason })
    })
  }

  catch(failCallback) {
    return this.then(undefined, failCallback)
  }

  static all(array) {
    let result = []
    let index = 0
    return new MyPromise((resolve, reject) => {
      function pushData (i, value) {
        result[i] = value
        index++
        if (index === array.length) {
          resolve(result)
        }
      }
      for (let i = 0; i < array.length; i++) {
        let current = array[i];
        if (current instanceof MyPromise) {
          // promise
          current.then(value => pushData(i, value), reason => reject(reason))
        } else {
          // 普通值
          pushData(i, array[i])
        }
      }
    })
  }

  static resolve(value) {
    // promise对象直接返回 否则创建promise
    if (value instanceof MyPromise) return value
    return new MyPromise((resolve => resolve(value)))
  }
}

function resolvePromsie(promise2, x, resolve, reject) {
  // 判断 promise 返回自己的情况
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if (x instanceof MyPromise) {
    // promise对象
    // 根据promise对象返回结果决定resolve reject
    x.then(resolve, reject)
  } else {
    // 普通值
    resolve(x)
  }
}

module.exports = MyPromise